package common;

import common.listener.Subscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import common.utils.CollectionUtils;
import common.utils.ConcurrentHashSet;
import common.utils.ThreadUtils;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import static common.NotifyCenter.ringBufferSize;

public class DefaultPublisher extends Thread implements EventPublisher {

    protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultPublisher.class);
    private volatile boolean initialized = false;

    private volatile boolean shutdown = false;

    private int queueMaxSize = -1;

    private BlockingQueue<common.Event> queue;

    protected volatile Long lastEventSequence = -1L;

    private static final AtomicReferenceFieldUpdater<DefaultPublisher, Long> UPDATER = AtomicReferenceFieldUpdater
            .newUpdater(DefaultPublisher.class, Long.class, "lastEventSequence");

    private Class<? extends Event> eventType;

    protected final ConcurrentHashSet<Subscriber> subscribers = new ConcurrentHashSet<>();

    public void init(Class<? extends Event> type, int bufferSize) {
        setDaemon(true);
        setName("nacos-publisher-" + type.getName());
        this.eventType = type;
        this.queueMaxSize = bufferSize;
        this.queue = new ArrayBlockingQueue<>(bufferSize);
        start();
    }

    @Override
    public synchronized void start() {
        if (!initialized) {
            super.start();
            if (queueMaxSize == -1) {
                queueMaxSize = ringBufferSize;
            }
            System.out.print("启动线程");
            initialized = true;
        }
    }


    private boolean hasSubscriber() {
        return CollectionUtils.isNotEmpty(subscribers);
    }

    @Override
    public long currentEventSize() {
        return queue.size();
    }

    @Override
    public void run() {
        System.out.print("启动子线程");
        openEventHandler();
    }

    private void openEventHandler() {
        try {

            int waitTimes = 60;
            for (; ; ) {//没有的话先等待1分钟
                if (shutdown || hasSubscriber() || waitTimes <= 0) {
                    break;
                }
                ThreadUtils.sleep(1000);
                waitTimes--;
            }
            for (; ; ) {
                if (shutdown) {
                    break;
                }
                //阻塞获取数据
                final Event event = queue.take();
                receiveEvent(event);
                UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
            }
        } catch (Exception ex) {
            LOGGER.error("common.Event common.listener exception :", ex);
        }
    }

     void receiveEvent(Event event) {
        final long currentEventSequence = event.sequence();
        if (!hasSubscriber()) {
            LOGGER.warn("[NotifyCenter] the {} is lost, because there is no subscriber.");
            return;
        }

        for (Subscriber subscriber : subscribers) {//如果事件过期了，则跳过
            if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) {
                LOGGER.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire",
                        event.getClass());
                continue;
            }

            notifySubscriber(subscriber, event);

        }
    }

    @Override
    public void notifySubscriber(Subscriber subscriber, Event event) {
        LOGGER.debug("[NotifyCenter] the {} will received by {}", event, subscriber);
        //执行订阅者event
        final Runnable job = () -> subscriber.onEvent(event);
        final Executor executor = subscriber.executor();
        if (executor != null) {//如果自己实现了executor则调用
            executor.execute(job);
        } else {
            try {
                job.run(); //如果没有，则当前线程直接调用
            } catch (Throwable e) {
                LOGGER.error("common.notify.Event callback exception: ", e);
            }

        }
    }

    void checkIsStart() {
        if (!initialized) {
            throw new IllegalArgumentException("Publisher does not start");
        }
    }

    @Override
    public void shutdown() {
        this.shutdown = true;
        this.queue.clear();
    }
    @Override
    public void addSubscriber(Subscriber subscriber) {
        subscribers.add(subscriber);
    }

    @Override
    public void removeSubscriber(Subscriber subscriber) {
        subscribers.remove(subscriber);
    }

    @Override
    public boolean publish(common.Event event) {
        checkIsStart();
        //入队
        boolean success = this.queue.offer(event);
        //如果没有入队
        if (!success) {
            LOGGER.warn("Unable to plug in due to interruption, synchronize sending time, event : {}", event);
            receiveEvent(event); //同步发送事件
            return true;
        }
        return false;
    }

}